/*
 * MainApplication.cpp
 *
 *  Created on: Mar 29, 2017
 *      Author: zhangjf
 */

#include <Poco/Types.h>
#include <Poco/Logger.h>
#include <Poco/AutoPtr.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/FileChannel.h>
#include <Poco/DateTimeFormatter.h>
#include <Poco/PatternFormatter.h>
#include <Poco/FormattingChannel.h>
#include "Poco/Delegate.h"

using namespace std;
using namespace Poco;
using namespace Poco::JSON;
using namespace Poco::Dynamic;

#include "Helper.h"
#include "JsonrpcProcessor.h"
#include "SessionManager.h"
#include "WebCallInterface.h"
#include <fcgi_stdio.h>
#include "WCIAppServConnector.h"
#include "MainApplication.h"

using namespace util;

Logger *pLogger = nullptr;

MainApplication::MainApplication()
{
	// TODO Auto-generated constructor stub

}

MainApplication::~MainApplication()
{
	// TODO Auto-generated destructor stub
}

void MainApplication::initLogger(const std::string& strLogName)
{
	AutoPtr<FileChannel> pChannel(new FileChannel);
	pChannel->setProperty("path", strLogName);
	pChannel->setProperty("rotation", " 20 M");
	pChannel->setProperty("archive", "timestamp");

	AutoPtr<PatternFormatter> pPF(new PatternFormatter);
	pPF->setProperty("pattern", "%Y-%m-%d %H:%M:%S %s(%T): %t");
	pPF->setProperty("times","local");
	AutoPtr<FormattingChannel> pFC(new FormattingChannel(pPF, pChannel));
	logger().setChannel(pFC);

	pLogger = &logger();
}

void MainApplication::initialize(Application& self)
{
	Application::initialize(self);

	//logger().setLevel("debug");
	std::string strName = this->commandName() + ".log";
	initLogger(strName);
	//setLogger(Logger::get("ApplicationStartup"))
	SessionManager::initialize(SessionManager::SOCKET_MODE_CLIENT);
//	bool blConnected = false;
//	while (!blConnected)
//	{
//		try
//		{
//			WCIAppServConnector::connect();
//			blConnected = true;
//			Poco::Thread::sleep(1000);
//		}
//		catch (...)
//		{
//			poco_information(Application::instance().logger(), "connect to appserv failed.");
//		}
//	}
}

int MainApplication::main(const vector<std::string>& args)
{
	Object joResp;
	std::string strResp;
	std::string strReq;
	bool blNotification = false;
	JsonrpcProcessor processor;

	processor.doCheckParams += delegate(this, &MainApplication::onCheckParams);
	processor.doRunJsonrpcMethod += delegate(this, &MainApplication::onRunJsonrpcMethod);

	while (FCGI_Accept() >= 0)
	{
		joResp.clear();
		blNotification = false;

		try{
			if (readRequest(strReq, joResp) == true)
			{
				if (processor.process(strReq, joResp, blNotification) == false)
				{
					poco_information(Application::instance().logger(), "jsonrpc process error");
				}
			}
			if (!blNotification)
			{
				fprintf(stdout, "Content-Type: application/json; charset=utf-8\n\n");
				ostringstream oss;
				joResp.stringify(oss);
				strResp = oss.str();	//((DynamicStruct)joResp).toString();
				poco_information(Application::instance().logger(), "send response to http client: " + strResp);

				fprintf(stdout, "%s\n", strResp.c_str());
			}
		}
		catch (Exception& exc)
		{
			std::cout << exc.displayText() << std::endl;
		}
	}

	processor.doCheckParams -= delegate(this, &MainApplication::onCheckParams);
	processor.doRunJsonrpcMethod -= delegate(this, &MainApplication::onRunJsonrpcMethod);
	return Application::EXIT_OK;
}

void MainApplication::onCheckParams(const void* pSender, JsonrpcParams& params)
{
	int errCode = 0;
	std::string errMsg;

	params.blRet = false;
	do
	{
		if (params.strMethod != "Login")
		{
			if (!params.joParams->has("sid"))
			{
				errCode = -32600;
				errMsg = "Invalid Request : not found sid in params tag";
				break;
			}

			params.strSid = params.joParams->getValue<std::string>("sid");
			if (params.strMethod != "Logout")
			{
				std::string strErr;
				if (checkSid(params.strSid, strErr) == true)
				{
					poco_information(logger(), "remove sid from params section");
					params.joParams->remove("sid");
				}
				else
				{
					errCode = -32600;
					errMsg = "Reqeust failed : " + strErr;
					break;
				}
			}
		}

		params.blRet = true;
	} while(false);

	if (params.blRet == false)
	{
		Object joError;
		joError.set("code", errCode);
		joError.set("message", errMsg);

		params.joResult.set("jsonrpc", "2.0");
		params.joResult.set("error", joError);
		params.joResult.set("id", "null");
	}
}

void MainApplication::onRunJsonrpcMethod(const void* pSender, JsonrpcParams& params)
{
	WebCallInterface::Ptr interface = WebCallInterfaceFactory::createInterface(params.strMethod, params.joParams);

	if (interface->process() == true)
	{
		params.joResult = interface->getResult();
		params.blRet = true;
	}
	else
	{
		params.joResult = interface->getError();
		params.blRet = false;
	}
}

bool MainApplication::readRequest(std::string& strReq, Object& joResp)
{
	bool blRet = false;
	int errCode = 0;
	std::string errMsg;

	do
	{
		const char* ReqMethod = getenv("REQUEST_METHOD");
		if (NULL == ReqMethod)
		{
			errCode = -32003;
			errMsg = "Server internal error : REQUEST_METHOD is NULL";
			break;
		}

		std::string strReqMethod = ReqMethod;
		if (strReqMethod == "POST")
		{
			const char* pszLength = getenv("CONTENT_LENGTH");
			if (pszLength == NULL)
			{
				errCode = -32000;
				errMsg = "Server internal error : CONTENT_LENGTH is NULL";
				break;
			}

			unsigned int nLength = ::atoi(pszLength) + 1;

			char *pszContent = new char[nLength];
			fgets(pszContent, nLength, stdin);

			strReq.assign(pszContent, nLength - 1);
			poco_information(logger(), "receive request from http client: " + strReq);

			util::trim(strReq);
			delete [] pszContent;

			blRet = true;
		}
		else if (strReqMethod == "GET")
		{
			errCode = -32001;
			errMsg = "Server internal error : unsurpport request method: GET";
			break;
		}
		else
		{
			errCode = -32002;
			errMsg = "Server internal error : Invalid Request Method";
			break;
		}
	} while (false);

	if (!blRet)
	{
		Object joError;
		joError.set("code", errCode);
		joError.set("message", errMsg);

		joResp.set("jsonrpc", "2.0");
		joResp.set("error", joError);
		joResp.set("id", "null");
	}

	return blRet;
}

bool MainApplication::checkSid(std::string& sid, std::string& strErr)
{
	Object joReq;

	joReq.set("method", "Query");
	Object joParam;
	joParam.set("sid", sid);
	joReq.set("params", joParam);

	std::string strReq = ((DynamicStruct)joReq).toString();
	SessionManager::sendBytes(strReq.data(), strReq.length());

	char buffer[1024];
	UInt32 n = SessionManager::receiveBytes(buffer, sizeof(buffer) - 1);
	std::string strMsg(buffer, n);

	try
	{
		Parser parser;
		Var varResp = parser.parse(strMsg);
		Object::Ptr joResp = varResp.extract<Object::Ptr>();

		strErr = joResp->getValue<std::string>("result");
		if (strErr == "success")
			return true;
		else
		{
			return false;
		}
	}
	catch (Exception& exc)
	{
		return false;
	}

	return false;
}

int main(int argc, char** argv)
{
	Poco::AutoPtr<MainApplication> pApp = new MainApplication;
	try
	{
		pApp->init(argc, argv);
		return pApp->run();
	}
	catch (Poco::Exception& exc)
	{
		poco_information(pApp->logger(), exc.displayText());
		return Poco::Util::Application::EXIT_CONFIG;
	}

	return Application::EXIT_OK;
}
